/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

angular.module('flowableModeler')
    .controller('StencilController', ['$rootScope', '$scope', '$http', '$modal', '$timeout', '$window', 'editorManager',
                                      function ($rootScope, $scope, $http, $modal, $timeout, $window, editorManager) {

        // Property window toggle state
        $scope.propertyWindowState = {'collapsed': false};

        // Add reference to global header-config
        $scope.headerConfig = FLOWABLE.HEADER_CONFIG;

        $scope.propertyWindowState.toggle = function () {
            $scope.propertyWindowState.collapsed = !$scope.propertyWindowState.collapsed;
            $timeout(function () {
                $window.dispatchEvent(new Event("resize"));
            }, 100);
        };

        // Code that is dependent on an initialised Editor is wrapped in a promise for the editor
        $scope.editorFactory.promise.then(function () {
        
        	/* Build stencil item list */

            // Build simple json representation of stencil set
            var stencilItemGroups = [];

            // Helper method: find a group in an array
            var findGroup = function (name, groupArray) {
                for (var index = 0; index < groupArray.length; index++) {
                    if (groupArray[index].name === name) {
                        return groupArray[index];
                    }
                }
                return null;
            };

            // Helper method: add a new group to an array of groups
            var addGroup = function (groupName, groupArray) {
                var group = { name: groupName, items: [], paletteItems: [], groups: [], visible: true };
                groupArray.push(group);
                return group;
            };

            /*
             StencilSet items
             */
            var data = editorManager.getStencilData();

            var quickMenuDefinition = undefined;
            var ignoreForPaletteDefinition = undefined;
            
            if (data.namespace == 'http://b3mn.org/stencilset/cmmn1.1#') {
                quickMenuDefinition = ['HumanTask', 'Association'];
                ignoreForPaletteDefinition = ['CasePlanModel'];
                
            } else {
                quickMenuDefinition = ['UserTask', 'EndNoneEvent', 'ExclusiveGateway', 
                                         'CatchTimerEvent', 'ThrowNoneEvent', 'TextAnnotation',
                                         'SequenceFlow', 'Association'];
                                         
                ignoreForPaletteDefinition = ['SequenceFlow', 'MessageFlow', 'Association', 'DataAssociation', 'DataStore', 'SendTask'];
            }
             
            var quickMenuItems = [];
              
            var morphRoles = [];
            for (var i = 0; i < data.rules.morphingRules.length; i++) 
            {
                var role = data.rules.morphingRules[i].role;
                var roleItem = {'role': role, 'morphOptions': []};
                morphRoles.push(roleItem);
            }
          
            // Check all received items
            for (var stencilIndex = 0; stencilIndex < data.stencils.length; stencilIndex++) {
            
                // Check if the root group is the 'diagram' group. If so, this item should not be shown.
                var currentGroupName = data.stencils[stencilIndex].groups[0];
                if (currentGroupName === 'Diagram' || currentGroupName === 'Form') {
                    continue;  // go to next item
                }
                
                var removed = false;
                if (data.stencils[stencilIndex].removed) {
                    removed = true;
                }

                var currentGroup = undefined;
                if (!removed) {
                    // Check if this group already exists. If not, we create a new one

                    if (currentGroupName !== null && currentGroupName !== undefined && currentGroupName.length > 0) {

                        currentGroup = findGroup(currentGroupName, stencilItemGroups); // Find group in root groups array
                        if (currentGroup === null) {
                            currentGroup = addGroup(currentGroupName, stencilItemGroups);
                        }

                        // Add all child groups (if any)
                        for (var groupIndex = 1; groupIndex < data.stencils[stencilIndex].groups.length; groupIndex++) {
                            var childGroupName = data.stencils[stencilIndex].groups[groupIndex];
                            var childGroup = findGroup(childGroupName, currentGroup.groups);
                            if (childGroup === null) {
                                childGroup = addGroup(childGroupName, currentGroup.groups);
                            }

                            // The current group variable holds the parent of the next group (if any),
                            // and is basically the last element in the array of groups defined in the stencil item
                            currentGroup = childGroup;

                        }

                    }
                }
                
                // Construct the stencil item
                var stencilItem = {'id': data.stencils[stencilIndex].id,
                    'name': data.stencils[stencilIndex].title,
                    'description': data.stencils[stencilIndex].description,
                    'icon': data.stencils[stencilIndex].icon,
                    'type': data.stencils[stencilIndex].type,
                    'roles': data.stencils[stencilIndex].roles,
                    'removed': removed,
                    'customIcon': false,
                    'canConnect': false,
                    'canConnectTo': false,
                    'canConnectAssociation': false};
                
                if (data.stencils[stencilIndex].customIconId && data.stencils[stencilIndex].customIconId > 0) {
                    stencilItem.customIcon = true;
                    stencilItem.icon = data.stencils[stencilIndex].customIconId;
                }
                
                if (!removed) {
                    if (quickMenuDefinition.indexOf(stencilItem.id) >= 0) {
                      quickMenuItems[quickMenuDefinition.indexOf(stencilItem.id)] = stencilItem;
                    }
                }
                
                if (stencilItem.id === 'TextAnnotation' || stencilItem.id === 'BoundaryCompensationEvent') {
                  stencilItem.canConnectAssociation = true;
                }
                
                for (var i = 0; i < data.stencils[stencilIndex].roles.length; i++) {
                  var stencilRole = data.stencils[stencilIndex].roles[i];
                  if (data.namespace == 'http://b3mn.org/stencilset/cmmn1.1#') {
                      if (stencilRole === 'association_start') {
                        stencilItem.canConnect = true;
                      } else if (stencilRole === 'association_end') {
                        stencilItem.canConnectTo = true;
                      }
                      
                  } else {
                      if (stencilRole === 'sequence_start') {
                        stencilItem.canConnect = true;
                      } else if (stencilRole === 'sequence_end') {
                        stencilItem.canConnectTo = true;
                      }
                  }
                  
                  for (var j = 0; j < morphRoles.length; j++) {
                    if (stencilRole === morphRoles[j].role) {
                      if (!removed) {
                        morphRoles[j].morphOptions.push(stencilItem);
                      }
                      stencilItem.morphRole = morphRoles[j].role;
                      break;
                    }
                  }
                }

                if (currentGroup) {
                  // Add the stencil item to the correct group
                  currentGroup.items.push(stencilItem);
                  if (ignoreForPaletteDefinition.indexOf(stencilItem.id) < 0) {
                    currentGroup.paletteItems.push(stencilItem);
                  }

                } else {
                    // It's a root stencil element
                    if (!removed) {
                        stencilItemGroups.push(stencilItem);
                    }
                }
            }
            
            for (var i = 0; i < stencilItemGroups.length; i++)  {
              if (stencilItemGroups[i].paletteItems && stencilItemGroups[i].paletteItems.length == 0) {
                stencilItemGroups[i].visible = false;
              }
            }
            
            $scope.stencilItemGroups = stencilItemGroups;

            var containmentRules = [];
            for (var i = 0; i < data.rules.containmentRules.length; i++) {
                var rule = data.rules.containmentRules[i];
                containmentRules.push(rule);
            }
            $scope.containmentRules = containmentRules;
            
            // remove quick menu items which are not available anymore due to custom pallette
            var availableQuickMenuItems = [];
            for (var i = 0; i < quickMenuItems.length; i++) {
                if (quickMenuItems[i]) {
                    availableQuickMenuItems[availableQuickMenuItems.length] = quickMenuItems[i];
                }
            }
            
            $scope.quickMenuItems = availableQuickMenuItems;
            $scope.morphRoles = morphRoles;

            /*
             * Listen to selection change events: show properties
             */
            editorManager.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, function (event) {
                var shapes = event.elements;
                var canvasSelected = false;
                if (shapes && shapes.length == 0) {
                    shapes = [editorManager.getCanvas()];
                    canvasSelected = true;
                }
                if (shapes && shapes.length > 0) {

                    var selectedShape = shapes.first();
                    var stencil = selectedShape.getStencil();
                    
                    if ($rootScope.selectedElementBeforeScrolling && stencil.id().indexOf('BPMNDiagram') !== -1 && stencil.id().indexOf('CMMNDiagram') !== -1) {
                      // ignore canvas event because of empty selection when scrolling stops
                      return;
                    }
                    
                    if ($rootScope.selectedElementBeforeScrolling && $rootScope.selectedElementBeforeScrolling.getId() === selectedShape.getId()) {
                      $rootScope.selectedElementBeforeScrolling = null;
                      return;
                    }

                    // Store previous selection
                    $scope.previousSelectedShape = $scope.selectedShape;
                    
                    // Only do something if another element is selected (Oryx fires this event multiple times)
                    if ($scope.selectedShape !== undefined && $scope.selectedShape.getId() === selectedShape.getId()) {
                        if ($rootScope.forceSelectionRefresh) {
                            // Switch the flag again, this run will force refresh
                            $rootScope.forceSelectionRefresh = false;
                        } else {
                            // Selected the same element again, no need to update anything
                            return;
                        }
                    }

                    var selectedItem = {'title': '', 'properties': []};

                    if (canvasSelected) {
                        selectedItem.auditData = {
                            'author': $scope.modelData.createdByUser,
                            'createDate': $scope.modelData.createDate
                        };
                    }

                    // Gather properties of selected item
                    var properties = stencil.properties();
                    for (var i = 0; i < properties.length; i++) {
                        var property = properties[i];
                        if (property.popular() == false) continue;
                        var key = property.prefix() + "-" + property.id();

                        if (key === 'oryx-name') {
                            selectedItem.title = selectedShape.properties.get(key);
                        }

                        // First we check if there is a config for 'key-type' and then for 'type' alone
                        var propertyConfig = FLOWABLE.PROPERTY_CONFIG[key + '-' + property.type()];
                        if (propertyConfig === undefined || propertyConfig === null) {
                            propertyConfig = FLOWABLE.PROPERTY_CONFIG[property.type()];
                        }

                        if (propertyConfig === undefined || propertyConfig === null) {
                            console.log('WARNING: no property configuration defined for ' + key + ' of type ' + property.type());
                        } else {

                            if (selectedShape.properties.get(key) === 'true') {
                                selectedShape.properties.set(key, true);
                            }
                            
                            if (FLOWABLE.UI_CONFIG.showRemovedProperties == false && property.isHidden())
                            {
                              continue;
                            }

                            var currentProperty = {
                                'key': key,
                                'title': property.title(),
                                'description': property.description(),
                                'type': property.type(),
                                'mode': 'read',
                                'hidden': property.isHidden(),
                                'value': selectedShape.properties.get(key)
                            };
                            
                            if ((currentProperty.type === 'complex' || currentProperty.type === 'multiplecomplex') && currentProperty.value && currentProperty.value.length > 0) {
                                try {
                                    currentProperty.value = JSON.parse(currentProperty.value);
                                } catch (err) {
                                    // ignore
                                }
                            }

                            if (propertyConfig.readModeTemplateUrl !== undefined && propertyConfig.readModeTemplateUrl !== null) {
                                currentProperty.readModeTemplateUrl = propertyConfig.readModeTemplateUrl + '?version=' + $rootScope.staticIncludeVersion;
                            }
                            if (propertyConfig.writeModeTemplateUrl !== null && propertyConfig.writeModeTemplateUrl !== null) {
                              currentProperty.writeModeTemplateUrl = propertyConfig.writeModeTemplateUrl + '?version=' + $rootScope.staticIncludeVersion;
                            }

                            if (propertyConfig.templateUrl !== undefined && propertyConfig.templateUrl !== null) {
                                currentProperty.templateUrl = propertyConfig.templateUrl + '?version=' + $rootScope.staticIncludeVersion;
                                currentProperty.hasReadWriteMode = false;
                            }
                            else {
                                currentProperty.hasReadWriteMode = true;
                            }

                            if (currentProperty.value === undefined
                                || currentProperty.value === null
                                || currentProperty.value.length == 0) {
                                currentProperty.noValue = true;
                            }

                            selectedItem.properties.push(currentProperty);
                        }
                    }

                    // Need to wrap this in an $apply block, see http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
                    $scope.safeApply(function () {
                        $scope.selectedItem = selectedItem;
                        $scope.selectedShape = selectedShape;
                    });

                } else {
                    $scope.safeApply(function () {
                        $scope.selectedItem = {};
                        $scope.selectedShape = null;
                    });
                }
            });
            
            editorManager.registerOnEvent(ORYX.CONFIG.EVENT_SELECTION_CHANGED, function (event) {
              
              FLOWABLE.eventBus.dispatch(FLOWABLE.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS);
              var shapes = event.elements;
                
              if (shapes && shapes.length == 1) {

              	  var selectedShape = shapes.first();
              
                  var a = editorManager.getCanvas().node.getScreenCTM();
              
	              var absoluteXY = selectedShape.absoluteXY();
	              
	              absoluteXY.x *= a.a;
	              absoluteXY.y *= a.d;
	              
	              var additionalIEZoom = 1;
	              if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                        var ua = navigator.userAgent;
                        if (ua.indexOf('MSIE') >= 0) {
                            //IE 10 and below
                            var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
                            if (zoom !== 100) {
                                additionalIEZoom = zoom / 100
                            }
                        }
                  }
	                    
	              if (additionalIEZoom === 1) {
	                   absoluteXY.y = absoluteXY.y - jQuery("#canvasSection").offset().top + 5;
	                         absoluteXY.x = absoluteXY.x - jQuery("#canvasSection").offset().left;
	              
	              } else {
	                   var canvasOffsetLeft = jQuery("#canvasSection").offset().left;
	                   var canvasScrollLeft = jQuery("#canvasSection").scrollLeft();
	                   var canvasScrollTop = jQuery("#canvasSection").scrollTop();
	                   
	                   var offset = a.e - (canvasOffsetLeft * additionalIEZoom);
	                   var additionaloffset = 0;
	                   if (offset > 10) {
	                       additionaloffset = (offset / additionalIEZoom) - offset;
	                   }
	                   absoluteXY.y = absoluteXY.y - (jQuery("#canvasSection").offset().top * additionalIEZoom) + 5 + ((canvasScrollTop * additionalIEZoom) - canvasScrollTop);
	                         absoluteXY.x = absoluteXY.x - (canvasOffsetLeft * additionalIEZoom) + additionaloffset + ((canvasScrollLeft * additionalIEZoom) - canvasScrollLeft);
                  }
	              
	              var bounds = new ORYX.Core.Bounds(a.e + absoluteXY.x, a.f + absoluteXY.y, a.e + absoluteXY.x + a.a*selectedShape.bounds.width(), a.f + absoluteXY.y + a.d*selectedShape.bounds.height());
	              var shapeXY = bounds.upperLeft();
	              
	              var stencilItem = $scope.getStencilItemById(selectedShape.getStencil().idWithoutNs());
	              var morphShapes = [];
	              if (stencilItem && stencilItem.morphRole) {
	                  for (var i = 0; i < $scope.morphRoles.length; i++) {
						  if ($scope.morphRoles[i].role === stencilItem.morphRole) {
	                    	  morphShapes = $scope.morphRoles[i].morphOptions;
	                      }
	                  }
	              }
              
              	  var x = shapeXY.x;
            	  if (bounds.width() < 48) {
              		  x -= 24;
            	  }
              
              	  if (morphShapes && morphShapes.length > 0) {
                	// In case the element is not wide enough, start the 2 bottom-buttons more to the left
                	// to prevent overflow in the right-menu
                
                	var morphButton = document.getElementById('morph-button');
                	morphButton.style.display = "block";
                	morphButton.style.left = x + 24 +'px';
                	morphButton.style.top = (shapeXY.y+bounds.height() + 2) + 'px';
              	  }
              
              	  var deleteButton = document.getElementById('delete-button');
              	  deleteButton.style.display = "block";
              	  deleteButton.style.left = x + 'px';
              	  deleteButton.style.top = (shapeXY.y+bounds.height() + 2) + 'px';
              	  
              	  var editable = selectedShape._stencil._jsonStencil.id.endsWith('CollapsedSubProcess') ;
				  var editButton = document.getElementById('edit-button');
				  if (editable) {
				      editButton.style.display = "block";
 					  if (morphShapes && morphShapes.length > 0) {
						editButton.style.left = x + 24 + 24 + 'px';
				      } else {
					 	editButton.style.left = x + 24 +'px';
					  }
					  editButton.style.top = (shapeXY.y+bounds.height() + 2) + 'px';
					  
				  } else {
					  editButton.style.display = "none";
				  }
              
              	  if (stencilItem && (stencilItem.canConnect || stencilItem.canConnectAssociation)) {
	                var quickButtonCounter = 0;
	                var quickButtonX = shapeXY.x+bounds.width() + 5;
	                var quickButtonY = shapeXY.y;
	                jQuery('.Oryx_button').each(function(i, obj) {
	                  if (obj.id !== 'morph-button' && obj.id != 'delete-button' && obj.id !== 'edit-button') {
	                    quickButtonCounter++;
	                    if (quickButtonCounter > 3) {
	                      quickButtonX = shapeXY.x+bounds.width() + 5;
	                      quickButtonY += 24;
	                      quickButtonCounter = 1;
	                      
	                    } else if (quickButtonCounter > 1) {
	                      quickButtonX += 24;
	                    }
	                    
	                    obj.style.display = "block";
	                    obj.style.left = quickButtonX + 'px';
	                    obj.style.top = quickButtonY + 'px';
	                  }
	                });
                  }
               }
            });
            
            if (!$rootScope.stencilInitialized) {
              FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS, function (event) {
                jQuery('.Oryx_button').each(function(i, obj) {
                  obj.style.display = "none";
              });
              });

              /*
               * Listen to property updates and act upon them
               */
              FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED, function (event) {
                  if (event.property && event.property.key) {
                      // If the name property is been updated, we also need to change the title of the currently selected item
                      if (event.property.key === 'oryx-name' && $scope.selectedItem !== undefined && $scope.selectedItem !== null) {
                          $scope.selectedItem.title = event.newValue;
                      }

                      // Update "no value" flag
                      event.property.noValue = (event.property.value === undefined
                          || event.property.value === null
                          || event.property.value.length == 0);
                  }
              });
              
              FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_SHOW_VALIDATION_POPUP, function (event) {
                  // Method to open validation dialog
                  var showValidationDialog = function() {
                      $rootScope.currentValidationId = event.validationId;
                      $rootScope.isOnProcessLevel = event.onProcessLevel;

                      _internalCreateModal({template: 'editor-app/popups/validation-errors.html?version=' + Date.now()},  $modal, $rootScope);
                  };

                  showValidationDialog();
              });
              
              FLOWABLE.eventBus.addListener(FLOWABLE.eventBus.EVENT_TYPE_NAVIGATE_TO_PROCESS, function (event) {
                  var modelMetaData = editorManager.getBaseModelData();
                  $rootScope.editorHistory.push({
                        id: modelMetaData.modelId, 
                        name: modelMetaData.name, 
                        type: 'bpmnmodel'
                  });
                  
              	  $window.location.href = "../editor/#/editor/" + event.processId;
              });
              
              $rootScope.stencilInitialized = true;
            }
            
            $scope.morphShape = function() {
              $scope.safeApply(function () {
                
                var shapes = editorManager.getSelection();
                if (shapes && shapes.length == 1) {
                  $rootScope.currentSelectedShape = shapes.first();
                  var stencilItem = $scope.getStencilItemById($rootScope.currentSelectedShape.getStencil().idWithoutNs());
                  var morphShapes = [];
                  for (var i = 0; i < $scope.morphRoles.length; i++) {
                    if ($scope.morphRoles[i].role === stencilItem.morphRole) {
                    	morphShapes = $scope.morphRoles[i].morphOptions.slice();
                    }
                  }

                  // Method to open shape select dialog (used later on)
                        var showSelectShapeDialog = function()
                        {
                            $rootScope.morphShapes = morphShapes;
                            _internalCreateModal({
                                backdrop: false,
                                keyboard: true,
                                template: 'editor-app/popups/select-shape.html?version=' + Date.now()
                            },  $modal, $rootScope);
                        };

                        showSelectShapeDialog();
                }
              });
            };
            
            $scope.deleteShape = function() {
              FLOWABLE.TOOLBAR.ACTIONS.deleteItem({'$scope': $scope, 'editorManager': editorManager});
            };
            
            $scope.quickAddItem = function(newItemId) {
              $scope.safeApply(function () {
                
                var shapes = editorManager.getSelection();
                if (shapes && shapes.length == 1) {
                  $rootScope.currentSelectedShape = shapes.first();
                  
                  var containedStencil = undefined;
                  var stencilSets = editorManager.getStencilSets().values();
                  for (var i = 0; i < stencilSets.length; i++) {
                    var stencilSet = stencilSets[i];
                  	var nodes = stencilSet.nodes();
                  	for (var j = 0; j < nodes.length; j++) {
                    	if (nodes[j].idWithoutNs() === newItemId) {
                      	    containedStencil = nodes[j];
                      		break;
                    	}
                    }
                  }
                  
                  if (!containedStencil) return;
                  
                  var option = {type: $scope.currentSelectedShape.getStencil().namespace() + newItemId, namespace: $scope.currentSelectedShape.getStencil().namespace()};
                  option['connectedShape'] = $rootScope.currentSelectedShape;
                  option['parent'] = $rootScope.currentSelectedShape.parent;
                  option['containedStencil'] = containedStencil;
                
                  var args = { sourceShape: $rootScope.currentSelectedShape, targetStencil: containedStencil };
                  var targetStencil = editorManager.getRules().connectMorph(args);
                  
                  // Check if there can be a target shape
                  if (!targetStencil) { 
                  	return; 
                  }
                  
                  option['connectingType'] = targetStencil.id();

                  var command = new FLOWABLE.CreateCommand(option, undefined, undefined, editorManager.getEditor());
                
                  editorManager.executeCommands([command]);
                }
              });
            };
            
            $scope.editShape = function(){
				editorManager.edit($scope.selectedShape.resourceId);
			};

        }); // end of $scope.editorFactory.promise block

        /* Click handler for clicking a property */
        $scope.propertyClicked = function (index) {
            if (!$scope.selectedItem.properties[index].hidden) {
                $scope.selectedItem.properties[index].mode = "write";
            }
        };

        /* Helper method to retrieve the template url for a property */
        $scope.getPropertyTemplateUrl = function (index) {
            return $scope.selectedItem.properties[index].templateUrl;
        };
        $scope.getPropertyReadModeTemplateUrl = function (index) {
            return $scope.selectedItem.properties[index].readModeTemplateUrl;
        };
        $scope.getPropertyWriteModeTemplateUrl = function (index) {
            return $scope.selectedItem.properties[index].writeModeTemplateUrl;
        };

        /* Method available to all sub controllers (for property controllers) to update the internal Oryx model */
        $scope.updatePropertyInModel = function (property, shapeId) {

            var shape = $scope.selectedShape;
            // Some updates may happen when selected shape is already changed, so when an additional
            // shapeId is supplied, we need to make sure the correct shape is updated (current or previous)
            if (shapeId) {
                if (shape.id != shapeId && $scope.previousSelectedShape && $scope.previousSelectedShape.id == shapeId) {
                    shape = $scope.previousSelectedShape;
                } else {
                    shape = null;
                }
            }

            if (!shape) {
                // When no shape is selected, or no shape is found for the alternative
                // shape ID, do nothing
                return;
            }
            var key = property.key;
            var newValue = property.value;
            var oldValue = shape.properties.get(key);

            if (newValue != oldValue) {
                var commandClass = ORYX.Core.Command.extend({
                    construct: function () {
                        this.key = key;
                        this.oldValue = oldValue;
                        this.newValue = newValue;
                        this.shape = shape;
                        this.facade = editorManager.getEditor();
                    },
                    execute: function () {
                        this.shape.setProperty(this.key, this.newValue);
                        this.facade.getCanvas().update();
                        this.facade.updateSelection();
                    },
                    rollback: function () {
                        this.shape.setProperty(this.key, this.oldValue);
                        this.facade.getCanvas().update();
                        this.facade.updateSelection();
                    }
                });
                // Instantiate the class
                var command = new commandClass();

                // Execute the command
                editorManager.executeCommands([command]);
                editorManager.handleEvents({
                    type: ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED,
                    elements: [shape],
                    key: key
                });

                // Switch the property back to read mode, now the update is done
                property.mode = 'read';

                // Fire event to all who is interested
                // Fire event to all who want to know about this
                var event = {
                    type: FLOWABLE.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED,
                    property: property,
                    oldValue: oldValue,
                    newValue: newValue
                };
                FLOWABLE.eventBus.dispatch(event.type, event);
            } else {
                // Switch the property back to read mode, no update was needed
                property.mode = 'read';
            }

        };

        /**
         * Helper method that searches a group for an item with the given id.
         * If not found, will return undefined.
         */
        $scope.findStencilItemInGroup = function (stencilItemId, group) {

            var item;

            // Check all items directly in this group
            for (var j = 0; j < group.items.length; j++) {
                item = group.items[j];
                if (item.id === stencilItemId) {
                    return item;
                }
            }

            // Check the child groups
            if (group.groups && group.groups.length > 0) {
                for (var k = 0; k < group.groups.length; k++) {
                    item = $scope.findStencilItemInGroup(stencilItemId, group.groups[k]);
                    if (item) {
                        return item;
                    }
                }
            }

            return undefined;
        };

        /**
         * Helper method to find a stencil item.
         */
        $scope.getStencilItemById = function (stencilItemId) {
            for (var i = 0; i < $scope.stencilItemGroups.length; i++) {
                var element = $scope.stencilItemGroups[i];

                // Real group
                if (element.items !== null && element.items !== undefined) {
                    var item = $scope.findStencilItemInGroup(stencilItemId, element);
                    if (item) {
                        return item;
                    }
                } else { // Root stencil item
                    if (element.id === stencilItemId) {
                        return element;
                    }
                }
            }
            return undefined;
        };

        /*
         * DRAG AND DROP FUNCTIONALITY
         */

        $scope.dropCallback = function (event, ui) {
          
            editorManager.handleEvents({
                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                highlightId: "shapeRepo.attached"
            });
            editorManager.handleEvents({
                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                highlightId: "shapeRepo.added"
            });
            
            editorManager.handleEvents({
                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                highlightId: "shapeMenu"
            });
            
            FLOWABLE.eventBus.dispatch(FLOWABLE.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS);

            if ($scope.dragCanContain) {

              var item = $scope.getStencilItemById(ui.draggable[0].id);
              
              var pos = {x: event.pageX, y: event.pageY};
              
              var additionalIEZoom = 1;
              if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                var ua = navigator.userAgent;
                if (ua.indexOf('MSIE') >= 0) {
                  //IE 10 and below
                  var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
                  if (zoom !== 100) {
                    additionalIEZoom = zoom / 100;
                  }
                }
              }
              
              var screenCTM = editorManager.getCanvas().node.getScreenCTM();

              // Correcting the UpperLeft-Offset
              pos.x -= (screenCTM.e / additionalIEZoom);
              pos.y -= (screenCTM.f / additionalIEZoom);
              // Correcting the Zoom-Factor
              pos.x /= screenCTM.a;
              pos.y /= screenCTM.d;
                
              // Correcting the ScrollOffset
              pos.x -= document.documentElement.scrollLeft;
              pos.y -= document.documentElement.scrollTop;
                
              var parentAbs = $scope.dragCurrentParent.absoluteXY();
              pos.x -= parentAbs.x;
              pos.y -= parentAbs.y;

              var containedStencil = undefined;
              var stencilSets = editorManager.getStencilSets().values();
              for (var i = 0; i < stencilSets.length; i++) {
                var stencilSet = stencilSets[i];
                var nodes = stencilSet.nodes();
                for (var j = 0; j < nodes.length; j++) {
                  if (nodes[j].idWithoutNs() === ui.draggable[0].id) {
                    containedStencil = nodes[j];
                    break;
                  }
                }

                if (!containedStencil) {
                  var edges = stencilSet.edges();
                  for (var j = 0; j < edges.length; j++) {
                  if (edges[j].idWithoutNs() === ui.draggable[0].id) {
                    containedStencil = edges[j];
                    break;
                  }
                }
              }
            }

            if (!containedStencil) return;

            if ($scope.quickMenu) {
              var shapes = editorManager.getSelection();
              if (shapes && shapes.length == 1) {
                var currentSelectedShape = shapes.first();

                var option = {};
                option.type = currentSelectedShape.getStencil().namespace() + ui.draggable[0].id;
                option.namespace = currentSelectedShape.getStencil().namespace();
                option.connectedShape = currentSelectedShape;
                option.parent = $scope.dragCurrentParent;
                option.containedStencil = containedStencil;
	                
                // If the ctrl key is not pressed, 
                // snapp the new shape to the center 
                // if it is near to the center of the other shape
                if (!event.ctrlKey) {
                  // Get the center of the shape
                  var cShape = currentSelectedShape.bounds.center();
                  // Snapp +-20 Pixel horizontal to the center 
                  if (20 > Math.abs(cShape.x - pos.x)) {
                    pos.x = cShape.x;
                  }
                  // Snapp +-20 Pixel vertical to the center 
                  if (20 > Math.abs(cShape.y - pos.y)) {
                    pos.y = cShape.y;
                  }
                }
	                
                option.position = pos;
	              
                if (containedStencil.idWithoutNs() !== 'SequenceFlow' && containedStencil.idWithoutNs() !== 'Association' && 
                        containedStencil.idWithoutNs() !== 'MessageFlow' && containedStencil.idWithoutNs() !== 'DataAssociation') {
	                        
                  var args = { sourceShape: currentSelectedShape, targetStencil: containedStencil };
                  var targetStencil = editorManager.getRules().connectMorph(args);
                  if (!targetStencil) { // Check if there can be a target shape
                  	return; 
                  }
                  option.connectingType = targetStencil.id();
                }
	  
                var command = new FLOWABLE.CreateCommand(option, $scope.dropTargetElement, pos, editorManager.getEditor());
	              
                editorManager.executeCommands([command]);
              }
	                
            } else {
              var canAttach = false;
              if (containedStencil.idWithoutNs() === 'BoundaryErrorEvent' || containedStencil.idWithoutNs() === 'BoundaryTimerEvent' ||
                    containedStencil.idWithoutNs() === 'BoundarySignalEvent' || containedStencil.idWithoutNs() === 'BoundaryMessageEvent' ||
                    containedStencil.idWithoutNs() === 'BoundaryCancelEvent' || containedStencil.idWithoutNs() === 'BoundaryCompensationEvent') {
                    
                // Modify position, otherwise boundary event will get position related to left corner of the canvas instead of the container
                pos = editorManager.eventCoordinates( event );
                canAttach = true;
              }

              var option = {};
              option['type'] = $scope.modelData.model.stencilset.namespace + item.id;
              option['namespace'] = $scope.modelData.model.stencilset.namespace;
              option['position'] = pos;
              option['parent'] = $scope.dragCurrentParent;

              var commandClass = ORYX.Core.Command.extend({
                    construct: function(option, dockedShape, canAttach, position, facade){
                        this.option = option;
                        this.docker = null;
                        this.dockedShape = dockedShape;
                        this.dockedShapeParent = dockedShape.parent || facade.getCanvas();
                        this.position = position;
                        this.facade = facade;
                        this.selection = this.facade.getSelection();
                        this.shape = null;
                        this.parent = null;
                        this.canAttach = canAttach;
                    },
                    execute: function(){
                        if (!this.shape) {
                            this.shape = this.facade.createShape(option);
                            this.parent = this.shape.parent;
                        } else if (this.parent) {
                            this.parent.add(this.shape);
                        }

                        if (this.canAttach && this.shape.dockers && this.shape.dockers.length) {
                            this.docker = this.shape.dockers[0];

                            this.dockedShapeParent.add(this.docker.parent);

                            // Set the Docker to the new Shape
                            this.docker.setDockedShape(undefined);
                            this.docker.bounds.centerMoveTo(this.position);
                            if (this.dockedShape !== this.facade.getCanvas()) {
                                this.docker.setDockedShape(this.dockedShape);
                            }
                            this.facade.setSelection( [this.docker.parent] );
                        }

                        this.facade.getCanvas().update();
                        this.facade.updateSelection();

                    },
                    rollback: function(){
                        if (this.shape) {
                            this.facade.setSelection(this.selection.without(this.shape));
                            this.facade.deleteShape(this.shape);
                        }
                        if (this.canAttach && this.docker) {
                            this.docker.setDockedShape(undefined);
                        }
                        this.facade.getCanvas().update();
                        this.facade.updateSelection();

                    }
              });

              // Update canvas
              var command = new commandClass(option, $scope.dragCurrentParent, canAttach, pos, editorManager.getEditor());
              editorManager.executeCommands([command]);

              // Fire event to all who want to know about this
              var dropEvent = {
                  type: FLOWABLE.eventBus.EVENT_TYPE_ITEM_DROPPED,
                  droppedItem: item,
                  position: pos
              };
              FLOWABLE.eventBus.dispatch(dropEvent.type, dropEvent);
            }
          }

          $scope.dragCurrentParent = undefined;
          $scope.dragCurrentParentId = undefined;
          $scope.dragCurrentParentStencil = undefined;
          $scope.dragCanContain = undefined;
          $scope.quickMenu = undefined;
          $scope.dropTargetElement = undefined;
        };


        $scope.overCallback = function (event, ui) {
            $scope.dragModeOver = true;
        };

        $scope.outCallback = function (event, ui) {
            $scope.dragModeOver = false;
        };

        $scope.startDragCallback = function (event, ui) {
            $scope.dragModeOver = false;
            $scope.quickMenu = false;
            if (!ui.helper.hasClass('stencil-item-dragged')) {
                ui.helper.addClass('stencil-item-dragged');
            }
        };
        
        $scope.startDragCallbackQuickMenu = function (event, ui) {
            $scope.dragModeOver = false;
            $scope.quickMenu = true;
        };
        
        $scope.dragCallback = function (event, ui) {
          
            if ($scope.dragModeOver != false) {
              
                var coord = editorManager.eventCoordinatesXY(event.pageX, event.pageY);
                
                var additionalIEZoom = 1;
                if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                    var ua = navigator.userAgent;
                    if (ua.indexOf('MSIE') >= 0) {
                        //IE 10 and below
                        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
                        if (zoom !== 100) {
                            additionalIEZoom = zoom / 100
                        }
                    }
                }
                
                if (additionalIEZoom !== 1) {
                     coord.x = coord.x / additionalIEZoom;
                     coord.y = coord.y / additionalIEZoom;
                }
                
                var aShapes = editorManager.getCanvas().getAbstractShapesAtPosition(coord);
                
                if (aShapes.length <= 0) {
                    if (event.helper) {
                        $scope.dragCanContain = false;
                        return false;
                    }
                }

                if (aShapes[0] instanceof ORYX.Core.Canvas) {
                    editorManager.getCanvas().setHightlightStateBasedOnX(coord.x);
                }

                if (aShapes.length == 1 && aShapes[0] instanceof ORYX.Core.Canvas) {
                  	var item = $scope.getStencilItemById(event.target.id);
                    var parentCandidate = aShapes[0];

                    if (item.id === 'Lane' || item.id === 'BoundaryErrorEvent' || item.id === 'BoundaryMessageEvent' || 
                            item.id === 'BoundarySignalEvent' || item.id === 'BoundaryTimerEvent' ||
                            item.id === 'BoundaryCancelEvent' || item.id === 'BoundaryCompensationEvent' || 
                            item.id === 'EntryCriterion') {
                        
                        $scope.dragCanContain = false;
                        
                        // Show Highlight
                        editorManager.handleEvents({
                            type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
                            highlightId: 'shapeRepo.added',
                            elements: [parentCandidate],
                            style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
                            color: ORYX.CONFIG.SELECTION_INVALID_COLOR
                        });
                        
                    } else {
                        $scope.dragCanContain = true;
                        $scope.dragCurrentParent = parentCandidate;
                        $scope.dragCurrentParentId = parentCandidate.id;
                        
                        editorManager.handleEvents({
                            type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                            highlightId: "shapeRepo.added"
                        });
                    }

                    editorManager.handleEvents({
                        type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                        highlightId: "shapeRepo.attached"
                    });
                    
                    return false;
                    
                } else  {
                    var item = $scope.getStencilItemById(event.target.id);
                    
                    var parentCandidate = aShapes.reverse().find(function (candidate) {
                        return (candidate instanceof ORYX.Core.Canvas
                            || candidate instanceof ORYX.Core.Node
                            || candidate instanceof ORYX.Core.Edge);
                    });
                    
                    if (!parentCandidate) {
                        $scope.dragCanContain = false;
                        return false;
                    }
                    
                    if (item.type === "node") {
                        
                        // check if the draggable is a boundary event and the parent an Activity
                        var _canContain = false;
                        var parentStencilId = parentCandidate.getStencil().id();

                        if ($scope.dragCurrentParentId && $scope.dragCurrentParentId === parentCandidate.id) {
                            return false;
                        }

                        var parentItem = $scope.getStencilItemById(parentCandidate.getStencil().idWithoutNs());
                        if (parentItem.roles.indexOf('Activity') > -1) {
                            if (item.roles.indexOf('IntermediateEventOnActivityBoundary') > -1 
                                || item.roles.indexOf('EntryCriterionOnItemBoundary') > -1
                                || item.roles.indexOf('ExitCriterionOnItemBoundary') > -1) {
                                _canContain = true;
                            }
                            
                        } else if(parentItem.roles.indexOf('StageActivity') > -1) {
                            if (item.roles.indexOf('EntryCriterionOnItemBoundary') > -1
                                || item.roles.indexOf('ExitCriterionOnItemBoundary') > -1) {
                                _canContain = true;
                            }
                        
                        } else if(parentItem.roles.indexOf('StageModelActivity') > -1) { 
                            if (item.roles.indexOf('ExitCriterionOnItemBoundary') > -1) {
                                _canContain = true;
                            }
                        
                        } else if (parentCandidate.getStencil().idWithoutNs() === 'Pool') {
                          	if (item.id === 'Lane') {
                            	_canContain = true;
                          	}
                        }
                        
                        if (_canContain) {
                            editorManager.handleEvents({
                                type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
                                highlightId: "shapeRepo.attached",
                                elements: [parentCandidate],
                                style: ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
                                color: ORYX.CONFIG.SELECTION_VALID_COLOR
                            });

                            editorManager.handleEvents({
                                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                                highlightId: "shapeRepo.added"
                            });
                            
                        } else {
                            for (var i = 0; i < $scope.containmentRules.length; i++) {
                                var rule = $scope.containmentRules[i];
                                if (rule.role === parentItem.id) {
                                    for (var j = 0; j < rule.contains.length; j++) {
                                        if (item.roles.indexOf(rule.contains[j]) > -1) {
                                            _canContain = true;
                                            break;
                                        }
                                    }

                                    if (_canContain) {
                                        break;
                                    }
                                }
                            }
                            
                            // Show Highlight
                            editorManager.handleEvents({
                                type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
                                highlightId: 'shapeRepo.added',
                                elements: [parentCandidate],
                                color: _canContain ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
                            });

                            editorManager.handleEvents({
                                type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                                highlightId: "shapeRepo.attached"
                            });
                        }

                        $scope.dragCurrentParent = parentCandidate;
                        $scope.dragCurrentParentId = parentCandidate.id;
                        $scope.dragCurrentParentStencil = parentStencilId;
                        $scope.dragCanContain = _canContain;
                        
                    } else  { 
                      var canvasCandidate = editorManager.getCanvas();
                      var canConnect = false;
                      
                      var targetStencil = $scope.getStencilItemById(parentCandidate.getStencil().idWithoutNs());
	                  if (targetStencil) {
	                    var associationConnect = false;
	                    if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent')) {
	                        associationConnect = true;
	                    } else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') {
                            associationConnect = true;
                        }
	                    
	                    if (targetStencil.canConnectTo || associationConnect) {
	                      canConnect = true;
	                    }
	                  }
                      
                      //Edge
                      $scope.dragCurrentParent = canvasCandidate;
                      $scope.dragCurrentParentId = canvasCandidate.id;
                      $scope.dragCurrentParentStencil = canvasCandidate.getStencil().id();
                      $scope.dragCanContain = canConnect;
                        
                      // Show Highlight
                      editorManager.handleEvents({
                            type: ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
                            highlightId: 'shapeRepo.added',
                            elements: [canvasCandidate],
                            color: ORYX.CONFIG.SELECTION_VALID_COLOR
                      });

                      editorManager.handleEvents({
                            type: ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
                            highlightId: "shapeRepo.attached"
                      });
              		}
                }
            }
        };
        
        $scope.dragCallbackQuickMenu = function (event, ui) {
          
            if ($scope.dragModeOver != false) {
                var coord = editorManager.eventCoordinatesXY(event.pageX, event.pageY);
                
                var additionalIEZoom = 1;
                if (!isNaN(screen.logicalXDPI) && !isNaN(screen.systemXDPI)) {
                    var ua = navigator.userAgent;
                    if (ua.indexOf('MSIE') >= 0) {
                        //IE 10 and below
                        var zoom = Math.round((screen.deviceXDPI / screen.logicalXDPI) * 100);
                        if (zoom !== 100) {
                            additionalIEZoom = zoom / 100
                        }
                    }
                }
                
                if (additionalIEZoom !== 1) {
                     coord.x = coord.x / additionalIEZoom;
                     coord.y = coord.y / additionalIEZoom;
                }
                
                var aShapes = editorManager.getCanvas().getAbstractShapesAtPosition(coord);
               
                if (aShapes.length <= 0) {
                    if (event.helper) {
                        $scope.dragCanContain = false;
                        return false;
                    }
                }

                if (aShapes[0] instanceof ORYX.Core.Canvas) {
                    editorManager.getCanvas().setHightlightStateBasedOnX(coord.x);
                }
                
                var stencil = undefined;
                var stencilSets = editorManager.getStencilSets().values();
                for (var i = 0; i < stencilSets.length; i++) {
                	var stencilSet = stencilSets[i];
              		var nodes = stencilSet.nodes();
              		for (var j = 0; j < nodes.length; j++) {
                		if (nodes[j].idWithoutNs() === event.target.id) {
                  			stencil = nodes[j];
                  			break;
                		}
                    }
              
              		if (!stencil) {
                		var edges = stencilSet.edges();
                  		for (var j = 0; j < edges.length; j++) {
                    		if (edges[j].idWithoutNs() === event.target.id) {
                      			stencil = edges[j];
                      			break;
                    		}
                      	}
              		}
              	}
            
                var candidate = aShapes.last();
                
                var isValid = false;
                if (stencil.type() === "node")  {
            		//check containment rules
            		var canContain = editorManager.getRules().canContain({containingShape:candidate, containedStencil:stencil});
            
            		var parentCandidate = aShapes.reverse().find(function (candidate) {
                        return (candidate instanceof ORYX.Core.Canvas
                            || candidate instanceof ORYX.Core.Node
                            || candidate instanceof ORYX.Core.Edge);
                    });

                    if (!parentCandidate) {
                        $scope.dragCanContain = false;
                        return false;
                    }
            
            		$scope.dragCurrentParent = parentCandidate;
                    $scope.dragCurrentParentId = parentCandidate.id;
                    $scope.dragCurrentParentStencil = parentCandidate.getStencil().id();
                    $scope.dragCanContain = canContain;
                    $scope.dropTargetElement = parentCandidate;
                    isValid = canContain;
      
          		} else { //Edge
          
            		var shapes = editorManager.getSelection();
                	if (shapes && shapes.length == 1) {
                  		var currentSelectedShape = shapes.first();
                  		var curCan = candidate;
                  		var canConnect = false;
                  
                  		var targetStencil = $scope.getStencilItemById(curCan.getStencil().idWithoutNs());
                  		if (targetStencil) {
                    		var associationConnect = false;
                    		if (stencil.idWithoutNs() === 'Association' && (curCan.getStencil().idWithoutNs() === 'TextAnnotation' || curCan.getStencil().idWithoutNs() === 'BoundaryCompensationEvent')) {
                      			associationConnect = true;
                    		} else if (stencil.idWithoutNs() === 'DataAssociation' && curCan.getStencil().idWithoutNs() === 'DataStore') {
                        		associationConnect = true;
                    		}
                    
                    		if (targetStencil.canConnectTo || associationConnect) {
                    			while (!canConnect && curCan && !(curCan instanceof ORYX.Core.Canvas)) {
                      				candidate = curCan;
                      				//check connection rules
                      				canConnect = editorManager.getRules().canConnect({
	                                  sourceShape: currentSelectedShape, 
	                                  edgeStencil: stencil, 
	                                  targetShape: curCan
	                                }); 
                      				curCan = curCan.parent;
                    			}
                    		}
                  		}
                  		var parentCandidate = editorManager.getCanvas();
                
                		isValid = canConnect;
                		$scope.dragCurrentParent = parentCandidate;
                        $scope.dragCurrentParentId = parentCandidate.id;
                        $scope.dragCurrentParentStencil = parentCandidate.getStencil().id();
                		$scope.dragCanContain = canConnect;
                		$scope.dropTargetElement = candidate;
                	}   
            
          		} 

                editorManager.handleEvents({
          			type:   ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW, 
          			highlightId:'shapeMenu',
          			elements: [candidate],
          			color: isValid ? ORYX.CONFIG.SELECTION_VALID_COLOR : ORYX.CONFIG.SELECTION_INVALID_COLOR
        		});
            }
        };

    }]);

var FLOWABLE = FLOWABLE || {};
//create command for undo/redo
FLOWABLE.CreateCommand = ORYX.Core.Command.extend({
  construct: function(option, currentReference, position, facade){
    this.option = option;
    this.currentReference = currentReference;
    this.position = position;
    this.facade = facade;
    this.shape;
    this.edge;
    this.targetRefPos;
    this.sourceRefPos;
    /*
     * clone options parameters
     */
        this.connectedShape = option.connectedShape;
        this.connectingType = option.connectingType;
        this.namespace = option.namespace;
        this.type = option.type;
        this.containedStencil = option.containedStencil;
        this.parent = option.parent;
        this.currentReference = currentReference;
        this.shapeOptions = option.shapeOptions;
  },      
  execute: function(){
    
    if (this.shape) {
      if (this.shape instanceof ORYX.Core.Node) {
        this.parent.add(this.shape);
        if (this.edge) {
          this.facade.getCanvas().add(this.edge);
          this.edge.dockers.first().setDockedShape(this.connectedShape);
          this.edge.dockers.first().setReferencePoint(this.sourceRefPos);
          this.edge.dockers.last().setDockedShape(this.shape);
          this.edge.dockers.last().setReferencePoint(this.targetRefPos);
        }
        
        this.facade.setSelection([this.shape]);
        
      } else if (this.shape instanceof ORYX.Core.Edge) {
        this.facade.getCanvas().add(this.shape);
        this.shape.dockers.first().setDockedShape(this.connectedShape);
        this.shape.dockers.first().setReferencePoint(this.sourceRefPos);
      }
    }
    else {
      this.shape = this.facade.createShape(this.option);
      this.edge = (!(this.shape instanceof ORYX.Core.Edge)) ? this.shape.getIncomingShapes().first() : undefined;
    }
    
    if (this.currentReference && this.position) {
      
      if (this.shape instanceof ORYX.Core.Edge) {
      
        if (!(this.currentReference instanceof ORYX.Core.Canvas)) {
          this.shape.dockers.last().setDockedShape(this.currentReference);
          
          if (this.currentReference.getStencil().idWithoutNs() === 'TextAnnotation')
          {
            var midpoint = {};
            midpoint.x = 0;
            midpoint.y = this.currentReference.bounds.height() / 2;
            this.shape.dockers.last().setReferencePoint(midpoint);
          }
          else
          {
            this.shape.dockers.last().setReferencePoint(this.currentReference.bounds.midPoint());
          }
        }
        else {
          this.shape.dockers.last().bounds.centerMoveTo(this.position);
        }
        this.sourceRefPos = this.shape.dockers.first().referencePoint;
        this.targetRefPos = this.shape.dockers.last().referencePoint;
        
      } else if (this.edge){
        this.sourceRefPos = this.edge.dockers.first().referencePoint;
        this.targetRefPos = this.edge.dockers.last().referencePoint;
      }
    } else {
      var containedStencil = this.containedStencil;
      var connectedShape = this.connectedShape;
      var bc = connectedShape.bounds;
      var bs = this.shape.bounds;
      
      var pos = bc.center();
      if(containedStencil.defaultAlign()==="north") {
        pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height()/2);
      } else if(containedStencil.defaultAlign()==="northeast") {
        pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
        pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
      } else if(containedStencil.defaultAlign()==="southeast") {
        pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
        pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
      } else if(containedStencil.defaultAlign()==="south") {
        pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.height()/2);
      } else if(containedStencil.defaultAlign()==="southwest") {
        pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
        pos.y += (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
      } else if(containedStencil.defaultAlign()==="west") {
        pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width()/2);
      } else if(containedStencil.defaultAlign()==="northwest") {
        pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.width()/2);
        pos.y -= (bc.height() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER + (bs.height()/2);
      } else {
        pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET + (bs.width()/2);
      }
      
      // Move shape to the new position
      this.shape.bounds.centerMoveTo(pos);
      
      // Move all dockers of a node to the position
      if (this.shape instanceof ORYX.Core.Node){
        (this.shape.dockers||[]).each(function(docker){
          docker.bounds.centerMoveTo(pos);
        });
      }
      
      //this.shape.update();
      this.position = pos;
      
      if (this.edge){
        this.sourceRefPos = this.edge.dockers.first().referencePoint;
        this.targetRefPos = this.edge.dockers.last().referencePoint;
      }
    }
    
    this.facade.getCanvas().update();
    this.facade.updateSelection();

  },
  rollback: function(){
    this.facade.deleteShape(this.shape);
    if(this.edge) {
      this.facade.deleteShape(this.edge);
    }
    //this.currentParent.update();
    this.facade.setSelection(this.facade.getSelection().without(this.shape, this.edge));
  }
});
